Block

Block的理解

####什么是Block
带有自动变量的匿名函数

  • 自动变量
  • 匿名函数

####Block的使用

  1. 作为变量
1
2
3
4
5
6
//定义
returnType(^blockName)(Parameters) = ^(Parameters){
.....
};
//调用
blockName(Parameters);
  1. 作为属性
1
@property (nonatomic,copy)returnType (^blockName)(Parameters);
  1. 作为函数参数
1
2
3
- (void)testMethodWithHandler:(returnType (^blockName)(Parameters))block;

[self testMethodWithHandler:^returnType (Parameters){....}];
  1. 使用typedef
1
2
typedef returntype (^BlockName)(Parameters);
@property (nonatomic,copy)BlockName block;

####Block的本质

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define BLOCK_DESCRIPTOR_1 1
struct Block_descriptor_1 {
uintptr_t reserved;
uintptr_t size;
};

struct Block_layout {
void *isa;
volatile int32_t flags; // contains ref count
int32_t reserved;
void (*invoke)(void *, ...);
struct Block_descriptor_1 *descriptor;
// imported variables
};

objc中对象的定义:首地址是isa的结构体指针,所以block是对象类型

block的class类型:

  • _NSConcreteStackBlock 栈上创建的block
  • _NSConcreteMallocBlock 堆上创建的block
  • _NSConcreteGlobalBlock 作为全局变量的block
  • _NSConcreteWeakBlockVariable
  • _NSConcreteAutoBlock
  • _NSConcreteFinalizingBlock

#####_NSConcreteGlobalBlock
创建block时,就是声明struct,初始化struct的成员;执行block,就是调用struct的函数指针,编译完成之后,block中的代码会被提取出来,成为单独的C函数,调用这个C函数,并把struct指针传递过去.block的实际作用就相当于C语言的匿名函数.

全局block:block内部没有捕获任何外部变量就是一个全局block类型,调用block(),和C语言的函数无异,不需要考虑其生命周期

#####_NSConcreteStackBlock

当block内部引用了外部变量,当struct被创建时,它是存在于函数的栈帧上的,其class是固定的_NSConcreteStackBlock类型.其捕获的变量是会赋值到结构体的成员上,所以当block初始化完成之后,捕获的变量不能更改.
当函数返回时,函数的栈帧被销毁,这个block的内存也会被清除.所以当函数结束后仍然需要这个block时,就必须用Block_copy()方法将它拷贝到堆上,核心动作即(申请内存,将栈数据复制过去,将class改成_NSConcreteMallocBlock,向捕获的对象发送retain,增加block的引用计数)

block就是个匿名函数,只不过我们给了一个变量来引用这个匿名函数,在需要的时候调用。但是,栈block会随着函数栈帧的销毁而销毁,这样一来,我们用之前做引用的变量再去调用这么一块被销毁的内存,就会出现内存崩溃。